use crate::AppState;
use chat_core::{error::AppError, Chat, ChatType};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

#[derive(Debug, Clone, Default, ToSchema, Serialize, Deserialize)]
pub struct CreateChat {
    pub name: Option<String>,
    pub members: Vec<i64>,
    pub public: bool,
}

#[derive(Debug, Clone, Default, ToSchema, Serialize, Deserialize)]
pub struct UpdateChat {
    pub name: String,
    pub members: Vec<i64>,
    pub public: bool,
}

#[allow(dead_code)]
impl AppState {
    pub async fn update_chat(
        &self,
        update_chat: &UpdateChat,
        ws_id: i64,
        fullname: &str,
    ) -> Result<(), AppError> {
        sqlx::query(
            r#"
         UPDATE chats
         SET name = $1, members = $2, public = $3
         WHERE ws_id = $4
         AND created_name = $5
        "#,
        )
        .bind(&update_chat.name)
        .bind(&update_chat.members)
        .bind(update_chat.public)
        .bind(ws_id)
        .bind(fullname)
        .execute(&self.pool)
        .await?;
        Ok(())
    }
    pub async fn fetch_all_chats(&self, ws_id: i64) -> Result<Vec<Chat>, AppError> {
        let chats = sqlx::query_as(
            r#"
         SELECT * FROM chats
         WHERE ws_id = $1
        "#,
        )
        .bind(ws_id)
        .fetch_all(&self.pool)
        .await?;
        Ok(chats)
    }

    pub async fn create_chat(
        &self,
        create_chat: &CreateChat,
        ws_id: i64,
        user_name: &str,
    ) -> Result<Chat, AppError> {
        let len: usize = create_chat.members.len();
        //  validate members count
        if len < 2 {
            return Err(AppError::CreateChatError(
                "chat must have at least 2 members".to_string(),
            ));
        }
        let chat_name = &create_chat.name;
        if len > 10 && chat_name.is_none() {
            return Err(AppError::CreateChatError(
                "Group chat with more than 10 members must have a name".to_string(),
            ));
        }
        let members = self.fetch_chat_user_by_ids(&create_chat.members).await?;
        if members.len() != len {
            return Err(AppError::CreateChatError(
                " Some  members  do not exist".to_string(),
            ));
        }
        let (name, chat_type) = match (&chat_name, len) {
            (None, 2) => ("", ChatType::Single),
            (None, _) => ("", ChatType::Group),
            (Some(name), _) => {
                if create_chat.public {
                    (name.as_str(), ChatType::PublicChannel)
                } else {
                    (name.as_str(), ChatType::PrivateChannel)
                }
            }
        };
        //let members = json!(&create_chat.members).to_string();
        let now = chrono::Utc::now().naive_utc();
        let chat:Chat= sqlx::query_as(
            r#"
        INSERT INTO chats (name,type, members, ws_id, created_date, created_name, updated_date, updated_name)
        VALUES($1, $2, $3, $4, $5, $6, $7, $8)
        RETURNING id, name, type, members, ws_id, created_date, created_name, updated_date, updated_name
      "#,
        )
        .bind(name)
        .bind(chat_type)
        .bind(&create_chat.members)
        .bind(ws_id)
        .bind(now)
        .bind(user_name)
        .bind(now)
        .bind(user_name)
        .fetch_one(&self.pool)
        .await?;
        Ok(chat)
    }

    #[allow(dead_code)]
    pub async fn del_chat_by_ws_id(&self, ws_id: i64) -> Result<(), AppError> {
        sqlx::query("DELETE FROM chats WHERE ws_id = $1")
            .bind(ws_id)
            .execute(&self.pool)
            .await?;
        Ok(())
    }

    pub async fn fetch_chat_by_id(&self, id: i64) -> Result<Option<Chat>, AppError> {
        let chat = sqlx::query_as("SELECT * FROM chats WHERE id = $1")
            .bind(id)
            .fetch_optional(&self.pool)
            .await?;
        Ok(chat)
    }
    pub async fn del_chat_by_id(&self, id: i64) -> Result<(), AppError> {
        sqlx::query("DELETE FROM chats WHERE id = $1")
            .bind(id)
            .execute(&self.pool)
            .await?;
        Ok(())
    }
    pub async fn get_chat_by_id(&self, id: i64) -> Result<Option<Chat>, AppError> {
        let chat_opt = sqlx::query_as(
            r#"
        SELECT * FROM chats WHERE id = $1
        "#,
        )
        .bind(id)
        .fetch_optional(&self.pool)
        .await?;
        Ok(chat_opt)
    }
    pub async fn is_chat_member(&self, chat_id: i64, user_id: i64) -> Result<bool, AppError> {
        let ret = sqlx::query(
            r#"
          SELECT 1 FROM
           chats WHERE id = $1
            AND  $2 = ANY(members)
         "#,
        )
        .bind(chat_id)
        .bind(user_id)
        .fetch_optional(&self.pool)
        .await?;
        Ok(ret.is_some())
    }
}

impl CreateChat {
    pub fn new(name: Option<String>, members: Vec<i64>, public: bool) -> Self {
        Self {
            name,
            members,
            public,
        }
    }
}
#[cfg(test)]
mod tests {

    use crate::{models::chat::CreateChat, AppState};
    use chat_core::ChatType;
    use chat_core::{error::AppError, CreateUser};
    use jwt_simple::reexports::rand::{self, Rng};
    #[allow(unused_imports)]
    use sqlx::Executor;

    #[tokio::test]
    async fn test_create_chat() -> Result<(), AppError> {
        let chat_name = "test_chat_name";
        let mut members = Vec::new();
        let ws_id = 0;
        let state = AppState::new_for_test().await?;
        let mut emails = Vec::new();
        for _ in 0..3 {
            let mut thread_rng = rand::thread_rng();
            let random_number: u8 = thread_rng.gen();
            let fullname = format!("jinyulinlang{0}", random_number);
            let email = format!("jinyulinlang{0}@qq.com", random_number);
            let password = "123456".to_string();
            let create_user = CreateUser::new(fullname, email.clone(), ws_id, password);
            let user = state.create_user(&create_user).await?;
            members.push(user.id);
            emails.push(email);
        }
        let create_chat = CreateChat::new(Some(chat_name.to_string()), members, true);
        let chat = state.create_chat(&create_chat, 0, "jinyulinlang").await?;
        assert!(chat.id > 0);
        let member_size = chat.members.len();
        assert_eq!(member_size, 3);
        assert_eq!(chat.r#type, ChatType::PublicChannel);
        state.del_chat_by_ws_id(0).await?;
        for ele in emails {
            state.delete_user_by_email(&ele).await?;
        }
        Ok(())
    }

    #[tokio::test]
    async fn test_get_chat_by_id() -> Result<(), AppError> {
        let chat_name = "test_chat_name";
        let mut members = Vec::new();
        let ws_id = 0;
        let state = AppState::new_for_test().await?;
        let mut emails = Vec::new();
        // let mut tx: sqlx::Transaction<'_, sqlx::Postgres> = state.pool.begin().await?;
        // tx.execute("ALTER SEQUENCE IF EXISTS chats_id_seq RESTART WITH 1;")
        //     .await
        //     .expect("msg");
        // tx.commit().await?;
        for _ in 0..3 {
            let mut thread_rng = rand::thread_rng();
            let random_number: u8 = thread_rng.gen();
            let fullname = format!("jinyulinlang{0}", random_number);
            let email = format!("jinyulinlang{0}@qq.com", random_number);
            let password = "123456".to_string();
            let create_user = CreateUser::new(fullname, email.clone(), ws_id, password);
            let user = state.create_user(&create_user).await?;

            members.push(user.id);
            emails.push(email);
        }
        let create_chat = CreateChat::new(Some(chat_name.to_string()), members, true);
        let chat = state.create_chat(&create_chat, 0, "jinyulinlang").await?;
        assert!(chat.id > 0);
        let member_size = chat.members.len();
        assert_eq!(member_size, 3);
        assert_eq!(chat.r#type, ChatType::PublicChannel);

        let chat = state.get_chat_by_id(chat.id).await?;
        assert!(chat.is_some());
        state.del_chat_by_ws_id(0).await?;
        for ele in emails {
            state.delete_user_by_email(&ele).await?;
        }
        Ok(())
    }

    #[tokio::test]
    async fn test_is_chat_member() -> Result<(), AppError> {
        let state = AppState::new_for_test().await?;
        let mut thread_rng = rand::thread_rng();
        let random_number: u8 = thread_rng.gen();
        let fullname = format!("jinyulinlang{0}", random_number);
        let email = format!("jinyulinlang{0}@qq.com", random_number);
        let password = "123456".to_string();
        let create_user = CreateUser::new(fullname, email.clone(), 0, password);
        let user = state.create_user(&create_user).await?;
        let mut members = Vec::new();
        members.push(user.id);
        let random_number: u8 = thread_rng.gen();
        let fullname = format!("jinyulinlang{0}", random_number);
        let email2 = format!("jinyulinlang{0}@qq.com", random_number);
        let password = "123456".to_string();
        let create_user = CreateUser::new(fullname, email2.clone(), 0, password);
        let user = state.create_user(&create_user).await?;
        members.push(user.id);

        let create_chat = CreateChat::new(None, members, true);
        let chat = state.create_chat(&create_chat, 0, "jinyulinlang").await?;
        let is_member = state.is_chat_member(chat.id, user.id).await?;
        assert!(is_member);
        state.del_chat_by_ws_id(0).await?;
        state.delete_user_by_email(&email).await?;
        state.delete_user_by_email(&email2).await?;
        Ok(())
    }
}
